//step: 表示步骤项
//pane: 表示步骤内容项
var DEFAULTS = {
    config: [], //必传参数，步骤项与步骤内容项的配置，如[1,2,3]表示一共有三个（config.length）步骤，第1个步骤有1个（config[0]）内容项，第2个步骤有2个（config[1]）内容项，第3个步骤有3个（config[2]）内容项
    stepPanes: '', //必传参数，步骤内容项的jq 选择器
    navSteps: '', //必传参数，步骤项的jq 选择器
    initStepIndex: 1, //初始时显示的步骤位置，如果一共有4个步骤，该参数可选值为：1,2,3,4
    initPaneIndex: 1, //初始时显示的步骤内容项位置，基于initStepIndex，如果initStepIndex设置成2，且该步骤有3个内容项，则该参数可选值为：1,2,3
    onStepJump: $.noop, //步骤项跳转时候的回调
    onBeforePaneChange: $.noop, //步骤内容项切换之前的回调
    onPaneChange: $.noop, //步骤内容项切换之后的回调
    onPaneLoad: $.noop //步骤内容项第一次显示时的回调
};

function StepJump(options) {
    var opts = $.extend({}, DEFAULTS, options),
        $stepPanes = $(opts.stepPanes),
        $navSteps = $(opts.navSteps),
        config = opts.config,
        stepPaneCount = sum.apply(null, config), //步骤内容项的总数
        currentStep = opts.initStepIndex - 1, //当前步骤项的索引
        currentPane = sum.apply(null, config.slice(0, currentStep)) + (opts.initPaneIndex - 1), //当前内容项的索引
        maxStepIndex = currentStep, //允许通过直接点击步骤项跳转的最大步骤项位置
        $activePane = $stepPanes.eq(currentPane);

    //注册仅触发一次的stepLoad事件
    $stepPanes.each(function() {
        $(this).one('stepLoad', $.proxy(function() {
            opts.onPaneLoad.apply(this, [].slice.apply(arguments).concat([currentStep, currentPane]));
        }, this));
    });
    //初始化UI
    showStep(currentStep);
    $activePane.addClass('active').trigger('stepLoad');

    //注册点击步骤项的回调
    $navSteps.on('click.step.jump', function() {
        var $this = $(this),
            step = $this.index(opts.navSteps); //找到当前点击步骤项在所有步骤项中的位置

        if (step > maxStepIndex || $this.hasClass('current') || $this.hasClass('disabled')) return;
        //跳转到该步骤项的第一个步骤内容项
        goStep(step);
    });

    //步骤项UI控制
    function showStep(targetStep) {
        $navSteps.each(function(i) {
            var cname = this.className;
            cname = $.trim(cname.replace(/current|done/g, ''));

            if (i < targetStep) {
                //当前步骤之前的状态全部设置为done
                cname += ' done';
            } else if (i == targetStep) {
                //当前步骤项状态设置为current
                cname += ' current';
            }
            this.className = cname;
        });
    }

    function goStep(step) {
        go(getPaneCountBeforeStep(config, step));
    }

    //通过步骤内容项查找步骤项的位置
    function getStepByPaneIndex(targetPane) {
        var r = 0,
            targetStep = 0;
        for (var i = 0; i < stepPaneCount; i++) {
            r = r + config[i];
            if (targetPane < r) {
                targetStep = i;
                break;
            }
        }
        return targetStep;
    }

    function go(targetPane) {
        if (targetPane < 0 || targetPane >= stepPaneCount) {
            return;
        }

        //在切换步骤内容项之前提供给外部的回调，以便外部可以对当前步骤内容项做一些校验之类的工作
        //如果回调返回false则取消切换
        var ret = opts.onBeforePaneChange(currentPane, targetPane, currentStep);
        if (ret === false) return;

        var $targetPane = $stepPanes.eq(targetPane),
            targetStep = getStepByPaneIndex(targetPane);

        $activePane.removeClass('active');
        $targetPane.addClass('active');

        opts.onPaneChange(currentPane, targetPane, currentStep);

        $activePane = $targetPane;
        currentPane = targetPane;

        var oldStepIndex = currentStep;

        currentStep = targetStep;
        currentStep > maxStepIndex && (maxStepIndex = currentStep);

        $targetPane.trigger('stepLoad');

        if (targetStep !== oldStepIndex) {
            showStep(targetStep);
            opts.onStepJump(oldStepIndex, targetStep);
        }
    }

    return {
        goStep: function(step) {
            goStep(step - 1);
        },
        goNext: function() {
            go(currentPane + 1);
        },
        goPrev: function() {
            go(currentPane - 1);
        }
    }
}

//根据步骤内容项的绝对索引位置，获取相对于步骤项的位置
//step从0开始，pane表示绝对索引位置，比如stepPanes一共有6个，那么pane可能的值就是0-5
//举例：config: [1,3,1,1], step: 2, pane: 4，就会返回1，表示第三个步骤的第1个步骤内容项的位置
StepJump.getRelativePaneIndex = function(config, step, pane) {
    return pane - getPaneCountBeforeStep(config, step) + 1;
};

//求和
//注：slice(start,end)返回的数据不包含end索引对应的元素
function sum() {
    var a = [].slice.apply(arguments),
        r = 0;
    a.forEach(function(n) {
        r = r + n;
    });
    return r;
}

//统计在指定的步骤项之前一共有多少个步骤内容项，step从0开始，比如config: [1,3,1,1], 当step=2，就会返回4
function getPaneCountBeforeStep(config, step) {
    return sum.apply(null, config.slice(0, step));
}